package com.tools.commons.utils;

import com.google.common.collect.Sets;
import com.tools.commons.exceptions.GlobalException;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.util.Set;
import java.util.function.Function;

/**
 * @author wangminggen
 */
public class BeanUtils {
    public static void updateProperties(Object source, Object target) {
        Assert.notNull(source, "source object must not be null");
        Assert.notNull(target, "target object must not be null");
        try {
            // 忽略null值属性
            org.springframework.beans.BeanUtils.copyProperties(source, target, getNullPropertyNames(source));
        } catch (BeansException e) {
            throw new RuntimeException("Failed to copy properties", e);
        }
    }

    private static String[] getNullPropertyNames(Object source) {
        return getNullPropertyNameSet(source).toArray(new String[0]);
    }

    private static Set<String> getNullPropertyNameSet(Object source) {
        Assert.notNull(source, "source object must not be null");
        BeanWrapperImpl beanWrapper = new BeanWrapperImpl(source);
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        Set<String> emptyNames = Sets.newHashSet();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String propertyName = propertyDescriptor.getName();
            Object propertyValue = beanWrapper.getPropertyValue(propertyName);
            if (propertyValue == null) {
                emptyNames.add(propertyName);
            }
        }
        return emptyNames;
    }

    @Nullable
    public static <T> T transformFrom(@Nullable Object source, @NonNull Class<T> targetClass) {
        Assert.notNull(targetClass, "Target class must not be null");
        if (source == null) {
            return null;
        }
        try {
            T targetInstance = targetClass.newInstance();
            org.springframework.beans.BeanUtils.copyProperties(source, targetInstance, getNullPropertyNames(source));
            return targetInstance;
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalException("Failed to new " + targetClass.getName() + " instance or copy properties");
        }
    }

    @SuppressWarnings("unchecked")
    public static <T, R> R getOrDefault(T t, Function<T, R> f, R defaultValue) {
        if (t == null || f == null) {
            return defaultValue;
        }
        R value = f.apply(t);

        if (defaultValue instanceof String) {
            String val = (String) value;
            if (StringUtils.isNotBlank(val)) {
                return (R) val;
            }
        }
        return value != null ? value : defaultValue;
    }
}
